﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Text;
using System.Threading.Tasks;

namespace CNative.Utilities
{
    public class FastInvoke
    {
        public delegate object FastInvokeHandler(object target, object[] paramters);

        /// <summary>
        /// 反射对象类型,执行对象内部方法
        /// </summary>
        /// <param name="type">对象类型</param>
        /// <param name="methodName">方法名</param>
        /// <param name="paramters">参数</param>
        /// <returns>object</returns>
        public static object InvokeMethod(Type type, string methodName, params object[] paramters)
        {
            if (type == null)
                throw new Exception("对象类型 type 不能为NULL");
            var target = Activator.CreateInstance(type);
            return InvokeMethod(target, methodName, paramters);
        }
        /// <summary>
        /// 反射执行对象内部方法
        /// </summary>
        /// <param name="target">对象</param>
        /// <param name="methodName">方法名</param>
        /// <param name="paramters">参数</param>
        /// <returns>object</returns>
        public static object InvokeMethod(object target, string methodName, params object[] paramters)
        {
            if (target == null)
                throw new Exception("执行对象 target 不能为NULL");
            object _obj = new object();
            try
            {
                var _FgFastInvoke = GetMethodInvoker(target, methodName, paramters);
                _obj = _FgFastInvoke(target, paramters);
            }
            catch (ArgumentNullException ex)//方法未找到
            {
                throw new Exception(OutPutExectStr(methodName, paramters, ex, "插件异常(方法未找到)", target), ex);
            }
            catch (MethodAccessException ex)//方法有问题。
            {
                throw new Exception(OutPutExectStr(methodName, paramters, ex, "插件异常(方法未找到)", target), ex);
            }
            catch (MissingFieldException ex)//找不到该字段或属性。
            {
                throw new Exception(OutPutExectStr(methodName, paramters, ex, "插件异常(找不到该字段或属性)", target), ex);
            }
            catch (MissingMethodException ex)//方法或参数不匹配
            {
                throw new Exception(OutPutExectStr(methodName, paramters, ex, "插件异常(方法或参数不匹配)", target), ex);
            }
            catch (Exception ex)//其他错误
            {
                try
                {
                    var mi = GetMethodInfo(target.GetType(), methodName, paramters);
                    _obj = mi.Invoke(target, paramters);
                }
                catch
                {
                    throw new Exception(OutPutExectStr(methodName, paramters, ex, "插件异常(其他错误)", target), ex);
                }
            }
            return _obj;
        }
        private static string OutPutArgs(object[] args)
        {
            string _cs = string.Empty;
            foreach (var item in args)
                _cs += item?.ToString() + ",";
            return _cs;
        }
        private static string OutPutExectStr(string methodName, object[] args, Exception _exp, string _Extinfo = "", object target = null)
        {
            string _expStr = string.Empty;
            if (!string.IsNullOrEmpty(_Extinfo))
                _expStr = _Extinfo + "\r\t";
            _expStr += "控件：" + target?.GetType().Module.Name + "\r\t";
            _expStr += "类名：" + target?.GetType().FullName + "\r\t";
            _expStr += "方法：" + methodName + "\r\t";
            _expStr += "参数：" + OutPutArgs(args) + "\r\t";
            _expStr += "堆栈：" + _exp.ToString() + "\r\t";
            //Log(_expStr);
            return _expStr;
        }
        //-------------------------------------------------------------------------------------------------------------------------------------------------
        ///// <summary>
        ///// 反射内部方法
        ///// </summary>
        ///// <param name="_obj"></param>
        ///// <param name="_sMethodName"></param>
        ///// <param name="_iParamCount"></param>
        ///// <returns></returns>
        //public static FastInvokeHandler GetMethodInvoker(object _obj, string _sMethodName, int _iParamCount)
        //{
        //    return GetMethodInvoker(_obj.GetType(), _sMethodName, _iParamCount);
        //}
        ///// <summary>
        ///// 反射内部方法
        ///// </summary>
        ///// <param name="_Type"></param>
        ///// <param name="_sMethodName"></param>
        ///// <param name="_iParamCount"></param>
        ///// <returns></returns>
        //public static FastInvokeHandler GetMethodInvoker(Type _Type, string _sMethodName, int _iParamCount)
        //{
        //    return GetMethodInvoker(GetMethodInfo(_Type, _sMethodName, _iParamCount));
        //}
        /// <summary>
        /// 反射内部方法
        /// </summary>
        /// <param name="_obj"></param>
        /// <param name="_sMethodName"></param>
        /// <param name="_iParamCount"></param>
        /// <returns></returns>
        public static FastInvokeHandler GetMethodInvoker(object _obj, string _sMethodName, object[] paramters)
        {
            return GetMethodInvoker(_obj.GetType(), _sMethodName, paramters);
        }
        /// <summary>
        /// 反射内部方法
        /// </summary>
        /// <param name="_Type"></param>
        /// <param name="_sMethodName"></param>
        /// <param name="_iParamCount"></param>
        /// <returns></returns>
        public static FastInvokeHandler GetMethodInvoker(Type _Type, string _sMethodName, object[] paramters)
        {
            return GetMethodInvoker(GetMethodInfo(_Type, _sMethodName, paramters));
        }
        //---------------------------------------------------------------------------
        /// <summary>
        /// 反射内部方法
        /// </summary>
        /// <param name="methodInfo"></param>
        /// <returns></returns>
        public static FastInvokeHandler GetMethodInvoker(MethodInfo methodInfo)
        {
            if(methodInfo==null)
                throw new ArgumentNullException("methodInfo is null");
            DynamicMethod dynamicMethod = new DynamicMethod(string.Empty, typeof(object), new Type[] { typeof(object), typeof(object[]) }, methodInfo.DeclaringType.Module,true);
            ILGenerator il = dynamicMethod.GetILGenerator();
            ParameterInfo[] ps = methodInfo.GetParameters();
            Type[] paramTypes = new Type[ps.Length];
            for (int i = 0; i < paramTypes.Length; i++)
            {
                if (ps[i].ParameterType.IsByRef)
                    paramTypes[i] = ps[i].ParameterType.GetElementType();
                else
                    paramTypes[i] = ps[i].ParameterType;
            }
            LocalBuilder[] locals = new LocalBuilder[paramTypes.Length];

            for (int i = 0; i < paramTypes.Length; i++)
            {
                locals[i] = il.DeclareLocal(paramTypes[i], true);
            }
            for (int i = 0; i < paramTypes.Length; i++)
            {
                il.Emit(OpCodes.Ldarg_1);
                EmitFastInt(il, i);
                il.Emit(OpCodes.Ldelem_Ref);
                EmitCastToReference(il, paramTypes[i]);
                il.Emit(OpCodes.Stloc, locals[i]);
            }
            if (!methodInfo.IsStatic)
            {
                il.Emit(OpCodes.Ldarg_0);
            }
            for (int i = 0; i < paramTypes.Length; i++)
            {
                if (ps[i].ParameterType.IsByRef)
                    il.Emit(OpCodes.Ldloca_S, locals[i]);
                else
                    il.Emit(OpCodes.Ldloc, locals[i]);
            }
            if (methodInfo.IsStatic|| methodInfo.IsPrivate || methodInfo.IsFamily)
                il.EmitCall(OpCodes.Call, methodInfo, null);
            else
                il.EmitCall(OpCodes.Call, methodInfo, null);
            if (methodInfo.ReturnType == typeof(void))
                il.Emit(OpCodes.Ldnull);
            else
                EmitBoxIfNeeded(il, methodInfo.ReturnType);

            for (int i = 0; i < paramTypes.Length; i++)
            {
                if (ps[i].ParameterType.IsByRef)
                {
                    il.Emit(OpCodes.Ldarg_1);
                    EmitFastInt(il, i);
                    il.Emit(OpCodes.Ldloc, locals[i]);
                    if (locals[i].LocalType.IsValueType)
                        il.Emit(OpCodes.Box, locals[i].LocalType);
                    il.Emit(OpCodes.Stelem_Ref);
                }
            }

            il.Emit(OpCodes.Ret);
            FastInvokeHandler invoder = (FastInvokeHandler)dynamicMethod.CreateDelegate(typeof(FastInvokeHandler));
            return invoder;
        }

        private static void EmitCastToReference(ILGenerator il, System.Type type)
        {
            if (type.IsValueType)
            {
                il.Emit(OpCodes.Unbox_Any, type);
            }
            else
            {
                il.Emit(OpCodes.Castclass, type);
            }
        }

        private static void EmitBoxIfNeeded(ILGenerator il, System.Type type)
        {
            if (type.IsValueType)
            {
                il.Emit(OpCodes.Box, type);
            }
        }

        internal static MethodInfo GetMethodInfo(Type _Type, string _sMethodName, object[] paramters)
        {
            try
            {
                var bindingAttr = BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public;
                return _Type.GetMethod(_sMethodName, bindingAttr);
            }
            catch (AmbiguousMatchException)
            {
                Type[] argsTypes = GetTypeArray(paramters, out bool argExistsNull);
                return GetMethodInfo(_Type, _sMethodName, argsTypes, argExistsNull);
            }
        }
        internal static Type[]  GetTypeArray(object[] paramters ,out bool argExistsNull)
        {
            argExistsNull = false; 
            Type[] argsTypes = new Type[0];
            try
            {
                if (paramters?.Length > 0)
                {
                    argsTypes = new Type[paramters.Length]; //Type.GetTypeArray(paramters);
                    for (var i = 0; i < paramters.Length; i++)
                    {
                        if (paramters[i] != null)
                        {
                            argsTypes[i] = paramters[i].GetType();
                        }
                        else
                        {
                            argsTypes[i] = null;
                            argExistsNull = true;
                        }
                    }
                }
            }
            catch
            {
                argsTypes = new Type[0];
            }
            return argsTypes;
        }
        internal static MethodInfo GetMethodInfo(Type _Type, string _sMethodName, Type[] argsTypes, bool argExistsNull = false)
        {
            int _iParamCount = argsTypes == null ? 0 : argsTypes.Length;
            var bindingAttr = BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public;
            if (argExistsNull)//如果入参存在空值
            {
                //通过名称获取所有配置的方法
                var _listMethodInfo = _Type.GetMethods(bindingAttr).ToList().FindAll(p => p.Name.Equals(_sMethodName));
                if (_listMethodInfo?.Count == 1)//唯一直接返回
                {
                    return _listMethodInfo[0];
                }
                else if (_listMethodInfo?.Count > 1)
                {
                    var _listMethodInfoc = _listMethodInfo.FindAll(p => p.GetParameters().Length.Equals(_iParamCount));
                    if (_listMethodInfoc?.Count == 1)//通过入参数量来配置方法，唯一直接返回
                    {
                        return _listMethodInfoc[0];
                    }
                    else if (_listMethodInfoc?.Count > 1)
                    {
                        //寻找参数类型匹配最多的方法
                        var mathCount = new List<int>(new int[_listMethodInfoc.Count]);
                        for (var i = 0; i < _listMethodInfoc.Count; i++)
                        {
                            var prs = _listMethodInfoc[i].GetParameters();
                            mathCount[i] = 0;
                            for (var k = 0; k < prs.Length; k++)
                            {
                                if ((argsTypes[k] != null
                                         && (argsTypes[k] == prs[k].ParameterType || argsTypes[k] == Nullable.GetUnderlyingType(prs[k].ParameterType)))
                                     || (argsTypes[k] == null
                                         && (!prs[k].ParameterType.IsValueType || prs[k].ParameterType.IsNullable_())))
                                    mathCount[i]++;
                            }
                        }
                        return _listMethodInfoc[mathCount.IndexOf(mathCount.Max())];
                    }
                }
                return null;
            }
            //------------------------------------------------------------------------------------------------------------------
            if (_iParamCount > 0)
            {
                return _Type.GetMethod(_sMethodName, bindingAttr,//筛选条件
                         Type.DefaultBinder,//绑定
                         argsTypes,//参数类型
                         new ParameterModifier[] { new ParameterModifier(_iParamCount) }//参数个数
                          );
            }
            else
            {
                try
                {
                    return _Type.GetMethod(_sMethodName, bindingAttr);
                }
                catch (AmbiguousMatchException)
                {
                    return _Type.GetMethods(bindingAttr)?.FirstOrDefault(m => m.Name == _sMethodName && m.GetParameters()?.Length == 0);
                }
            }
        }

        private static void EmitFastInt(ILGenerator il, int value)
        {
            switch (value)
            {
                case -1:
                    il.Emit(OpCodes.Ldc_I4_M1);
                    return;
                case 0:
                    il.Emit(OpCodes.Ldc_I4_0);
                    return;
                case 1:
                    il.Emit(OpCodes.Ldc_I4_1);
                    return;
                case 2:
                    il.Emit(OpCodes.Ldc_I4_2);
                    return;
                case 3:
                    il.Emit(OpCodes.Ldc_I4_3);
                    return;
                case 4:
                    il.Emit(OpCodes.Ldc_I4_4);
                    return;
                case 5:
                    il.Emit(OpCodes.Ldc_I4_5);
                    return;
                case 6:
                    il.Emit(OpCodes.Ldc_I4_6);
                    return;
                case 7:
                    il.Emit(OpCodes.Ldc_I4_7);
                    return;
                case 8:
                    il.Emit(OpCodes.Ldc_I4_8);
                    return;
            }

            if (value > -129 && value < 128)
            {
                il.Emit(OpCodes.Ldc_I4_S, (SByte)value);
            }
            else
            {
                il.Emit(OpCodes.Ldc_I4, value);
            }
        }
    }
}
